#include "cluster_project.h"

nodesGroup *allocate_nodes_group(int n)
{
	nodesGroup *rVal = (nodesGroup *)malloc(sizeof(nodesGroup));
	if (rVal == NULL)
		return NULL;
	rVal->iGroupSize = n;
	rVal->Group = (int *)calloc(sizeof(int),n);
	if (rVal->Group == NULL)
		return NULL;
	rVal->isDividible = 1;
	return rVal;
}

void freeNodesGroup(nodesGroup** G)
{
	freeSafely((*G)->Group);
	freeSafely(*G);
}

int createTheTrivialDivision(LINK *divisibleGroups, int iGroupSize)
{
	int i;
	nodesGroup *G;
	/*Create the first group - the trivial division*/
	G = allocate_nodes_group(iGroupSize);
	if (G == NULL)
	{
		printf("Failed to allocate memory");
		return 0;
	}
	if (allocateIntVector(&(G->Group) , iGroupSize) == 0)
	{
		freeNodesGroup(&G);
		return 0;
	}
	for (i=0; i<iGroupSize; i++)
	{
		G->Group[i] = i;
	}

	createNewList(divisibleGroups, G);
	return 1;
}

int getNewDivisoin(sparse_matrix_lst *A,elem precision, nodesGroup *G,
				   nodesGroup **G1, nodesGroup **G2, elem *deltaQ)
{
	elem **GnrModMat, **ModMat, *S, improveDeltaQ;
	int *Kg, *Group;
	int iGroupSize = 0, i, M;
	sparse_matrix_lst *Ag;
	elem *u1, b1, *Fg;
	int iG1Size, iG2Size;
	int numOfIter = 0;

	Group = G->Group;
	iGroupSize = G->iGroupSize;

	if (CreateModularityMat(A, Group, iGroupSize, &ModMat, &Kg, &M) == 0)
	{
		return 0;
	}

	if (CreateGeneralizedModMat(ModMat, Group, 
		iGroupSize, &GnrModMat, &Fg) == 0)
	{
		freeMemForGetNewDivision(ModMat,GnrModMat,Kg,Fg,u1,S,iGroupSize,Ag);
		return 0;
	}

	if (GetAgFromA(A, &Ag, Group, iGroupSize) == 0)
	{
		freeMemForGetNewDivision(ModMat,GnrModMat,Kg,Fg,u1,S,iGroupSize,Ag);
		return 0;
	}

	if (calcLeadingEigenPair(Ag, GnrModMat, Kg, Fg, M, precision, &u1, &b1) == 0)
	{
		freeMemForGetNewDivision(ModMat,GnrModMat,Kg,Fg,u1,S,iGroupSize,Ag);
		return 0;
	}

	if (allocateVector(&S, iGroupSize) == 0)
	{
		freeMemForGetNewDivision(ModMat,GnrModMat,Kg,Fg,u1,S,iGroupSize,Ag);
		return 0;
	}

	if (spectralDivideGroup(iGroupSize, u1, b1, Ag, GnrModMat, Kg,
		Fg, M, deltaQ, S) == 0)
	{
		freeMemForGetNewDivision(ModMat,GnrModMat,Kg,Fg,u1,S,iGroupSize,Ag);
		return 0;
	}

	*G1 = allocate_nodes_group(iGroupSize);
	*G2 = allocate_nodes_group(iGroupSize);
	if (*G1 == NULL || *G2 == NULL)
	{
		printf("Failed to allocate memory");
		freeMemForGetNewDivision(ModMat,GnrModMat,Kg,Fg,u1,S,iGroupSize,Ag);
		return 0;
	}

	iG1Size = iG2Size = 0;
	for (i=0; i<iGroupSize; i++)
	{
		if (S[i] == S[0])
		{
			(*G1)->Group[iG1Size] = Group[i];
			iG1Size++;
		}
		else
		{
			(*G2)->Group[iG2Size] = Group[i];
			iG2Size++;
		}
	}
	(*G1)->Group = realloc((*G1)->Group, iG1Size*sizeof(int));
	(*G2)->Group = realloc((*G2)->Group, iG1Size*sizeof(int));
	if ((*G1)->Group == NULL || (*G2)->Group == NULL)
	{
		printf("Failed to allocate memory");
		freeMemForGetNewDivision(ModMat,GnrModMat,Kg,Fg,u1,S,iGroupSize,Ag);
		return 0;
	}

	/*Now we call the improve_div module*/
	CalculateNewDivisionGroups(A,ModMat,Kg,M,iGroupSize, G->Group,
		(*G1)->Group, S, (*G1)->iGroupSize, &((*G1)->Group),
		&((*G1)->iGroupSize), &((*G2)->Group), &((*G2)->iGroupSize), &improveDeltaQ, &numOfIter);
	/*End of improve_div module*/

	(*deltaQ) += improveDeltaQ;

	(*G1)->grade = G->grade + *deltaQ;//@@@
	(*G1)->numOfIter = G->numOfIter + numOfIter;//@@@
	(*G2)->grade = G->grade + *deltaQ;//@@@
	(*G2)->numOfIter = G->numOfIter + numOfIter;//@@@

	
	if (!IS_POSITIVE((*deltaQ)))/*G is undivisible*/
	{
		freeMemForGetNewDivision(ModMat,GnrModMat,Kg,Fg,u1,S,iGroupSize,Ag);
		return 2;/* return code 2 iff the original group is undivisible*/
	}

	freeMemForGetNewDivision(ModMat,GnrModMat,Kg,Fg,u1,S,iGroupSize,Ag);
	return 1;
}

int freeMemForGetNewDivision(elem **ModMat, elem ** GnrModMat,
							 int *Kg, elem *Fg, elem *u1, elem *S, 
							 int iGroupSize, sparse_matrix_lst *Ag)
{
	Free2dimensionalArray(ModMat, iGroupSize);
	freeSafely(Kg);
	Free2dimensionalArray(GnrModMat, iGroupSize);
	freeSafely(Fg);
	free_sparse_matrix_lst(Ag);
	freeSafely(u1);
	freeSafely(S);
	return 1;
}

int oneAlogrithm3Itteration(sparse_matrix_lst *A, LINK *divisibleGroups,
							LINK *undivisibleGroups, elem precision, int *numOfGroups, elem *deltaQ)
{
	nodesGroup *G, *G1, *G2;
	int iTemp;
	G = (*divisibleGroups)->d;
	iTemp = getNewDivisoin(A, precision, G, &G1, &G2, deltaQ);
	if (iTemp == 0)
	{
		freeNodesGroup(&G1);
		freeNodesGroup(&G2);
		return 0;
	}
	else if (iTemp == 2)/*G is undivisible*/
	{
		G->isDividible = 0;
		removeFromList(divisibleGroups, G);
		insertToList(undivisibleGroups, G);
		freeNodesGroup(&G1);//@@@
		freeNodesGroup(&G2);//@@@
		(*numOfGroups)++;
	}
	else /* iTemp == 1 iff there is a new division*/
	{
		removeFromList(divisibleGroups, G);

		if (G1->iGroupSize == 1)
		{
			G1->isDividible = 0;
			insertToList(undivisibleGroups, G1);
			(*numOfGroups)++;
		}
		else
		{
			insertToList(divisibleGroups, G1);
		}

		if (G2->iGroupSize == 1)
		{
			G2->isDividible = 0;
			insertToList(undivisibleGroups, G2);
			(*numOfGroups)++;
		}
		else
		{
			insertToList(divisibleGroups, G2);
		}

		freeNodesGroup(&G);
	}
	return 1;
}